﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace System.Text.RegularExpressions
{
  public class RegexBuilder
  {
    public enum MatchingBehavior
    {
      [Description("Greedy")]
      GREEDY,
      [Description("Lazy")]
      LAZY
    }

    public const string LINE_START = @"^";
    public const string LINE_END = @"$";
    public const string STRING_START = @"\A";
    public const string STRING_END = @"\z";

    #region Characters
    public const string WORD_BOUNDARY = @"\b";
    public const string END_OF_PREVIOUS_MATCH = @"\G";
    public const string WS = @"\s+";
    public const string OPTIONAL_WS = @"\s*";
    public const string DIGIT = @"\d";
    public const string WORD_CHAR = @"\w";
    public const string LF = @"\n";
    public const string CR = @"\r";
    public const string TAB = @"\t";
    public const string PIPE = @"|";
    #endregion

    private StringBuilder Pattern { get; set; }
    private int GroupCount { get; set; }

    #region Constructor

    public RegexBuilder()
    {
      Pattern = new StringBuilder();
    }

    public static RegexBuilder New
    {
      get
      {
        return new RegexBuilder();
      }
    }

    #endregion

    #region Append

    public RegexBuilder Append(string sPattern)
    {
      Pattern.Append(sPattern);
      return this;
    }

    public RegexBuilder Append(RegexBuilder oPattern)
    {
      return Append(oPattern.ToString());
    }

    public RegexBuilder AppendFormat(string sFormat, params object[] args)
    {
      Pattern.AppendFormat(sFormat, args);
      return this;
    }

    public RegexBuilder this[string sPattern]
    {
      get
      {
        Pattern.Append(sPattern);
        return this;
      }
    }

    public RegexBuilder this[RegexBuilder oPattern]
    {
      get
      {
        Pattern.Append(oPattern.ToString());
        return this;
      }
    }

    #endregion

    #region Anchors

    public RegexBuilder StringStart()
    {
      return Append(STRING_START);
    }

    public RegexBuilder StringEnd()
    {
      return Append(STRING_END);
    }

    public RegexBuilder StartLine()
    {
      return Append(LINE_START);
    }

    public RegexBuilder EndLine()
    {
      return Append(LINE_END);
    }

    public RegexBuilder WordBoundary()
    {
      return Append(WORD_BOUNDARY);
    }

    #endregion

    #region Options

    public RegexBuilder IgnoreCase()
    {
      return Append(@"(?i)");
    }

    #endregion

    #region Sets

    public RegexBuilder AnyIn(string sPattern)
    {
      return AppendFormat(@"[{0}]", sPattern);
    }

    public RegexBuilder AnyNotIn(string sPattern)
    {
      return AppendFormat(@"[^{0}]", sPattern);
    }

    #endregion

    #region Or

    /// <summary>
    /// Appends all options separated by a the pipe character ("|").
    /// </summary>
    /// <param name="asOptions"></param>
    /// <returns></returns>
    /// <remarks>
    /// Or("this","that","other") = this|that|other
    /// </remarks>
    public RegexBuilder Or(params string[] asOptions)
    {
      return Or(false, asOptions);
    }

    /// <summary>
    /// Appends all options separated by the pipe character ("|") and optionally can use a non-capturing group.
    /// </summary>
    /// <param name="fAddGroup"></param>
    /// <param name="asOptions"></param>
    /// <returns></returns>
    /// <remarks>
    /// Or(true, "this", "that", "other") = (?:this|that|other)
    /// Or(false, "this", "that", "other") = this|that|other
    /// </remarks>
    public RegexBuilder Or(bool fAddGroup, params string[] asOptions)
    {
      if (fAddGroup)
        return AppendFormat(@"(?:{0})", string.Join(PIPE, asOptions));

      return Append(string.Join(PIPE, asOptions));
    }

    #endregion

    #region Comment

    /// <summary>
    /// Appends the comment text in a comment group. Text inside of a comment group is ignored by the RegEx engine.
    /// </summary>
    /// <param name="sComment"></param>
    /// <returns></returns>
    /// <remarks>
    /// Comment("This is a comment inside of a regex pattern") = (?#This is a comment inside of a regex pattern)
    /// </remarks>
    public RegexBuilder Comment(string sComment)
    {
      return AppendFormat(@"(?#{0})", sComment);
    }

    public RegexBuilder Comment(string sComment, bool fInclude)
    {
      if (fInclude)
        return Comment(sComment);
      return this;
    }

    #endregion

    #region Optional

    /// <summary>
    /// Appends optional greedy "?" or optional lazy "??"
    /// </summary>
    /// <param name="eBehavior"></param>
    /// <returns></returns>
    public RegexBuilder Optional(MatchingBehavior eBehavior)
    {
      if (Pattern.ToString().EndsWith(@"\s+"))
      {
        Pattern.Remove(Pattern.Length - 1, 1);
        switch (eBehavior)
        {
          case MatchingBehavior.GREEDY:
            return Append("*");
          case MatchingBehavior.LAZY:
            return Append("*?");
        }
        return Append("*");
      }
      switch (eBehavior)
      {
        case MatchingBehavior.GREEDY:
          return Append("?");
        case MatchingBehavior.LAZY:
          return Append("??");
      }

      return Append("?");
    }

    public RegexBuilder Optional()
    {
      return Optional(MatchingBehavior.GREEDY);
    }

    /// <summary>
    /// Makes the preceding item optional. Behavior is greedy.
    /// </summary>
    /// <param name="sPattern">Pattern to optionally match</param>
    /// <returns>RegexBuilder with optional pattern appended</returns>
    /// <remarks>
    /// Appends the pattern inside of a non-capturing group if it is longer than one character.
    /// Optional("abc") = (?:abc)?
    /// Optional("a") = a?
    /// Optional("\s+") = \s*
    /// </remarks>
    private RegexBuilder OptionalGreedy(string sPattern)
    {
      if (sPattern.Equals(WS))
        return Append(OPTIONAL_WS);
      if (sPattern.Length == 1 || (sPattern.Length == 2 && sPattern.StartsWith(@"\")))
        return Append(sPattern + "?");
      return AppendFormat(@"(?:{0})?", sPattern);
    }

    /// <summary>
    /// Makes the preceding item optional. Behavior is lazy.
    /// </summary>
    /// <param name="sPattern">Pattern to optionally match</param>
    /// <returns>RegexBuilder with optional pattern appended</returns>
    /// <remarks>
    /// Appends the pattern inside of a non-capturing group if it is longer than one character.
    /// Optional("abc") = (?:abc)??
    /// Optional("a") = a??
    /// Optional("\s+") = \s*?
    /// </remarks>
    private RegexBuilder OptionalLazy(string sPattern)
    {
      if (sPattern.Equals(WS))
        return Append(OPTIONAL_WS + "?");
      if (sPattern.Length == 1 || (sPattern.Length == 2 && sPattern.StartsWith(@"\")))
        return Append(sPattern + "??");
      return AppendFormat(@"(?:{0})??", sPattern);
    }

    /// <summary>
    /// Makes the preceding item optional. Default behavior is greedy.
    /// </summary>
    /// <param name="sPattern">Pattern to optionally match</param>
    /// <returns>RegexBuilder with optional pattern appended</returns>
    /// <remarks>
    /// Appends the pattern inside of a non-capturing group if it is longer than one character.
    /// Optional("abc") = (?:abc)?
    /// Optional("a") = a?
    /// Optional("\s+") = \s*
    /// </remarks>
    public RegexBuilder Optional(string sPattern)
    {
      return OptionalGreedy(sPattern);
    }

    /// <summary>
    /// Makes the preceding item optional.
    /// </summary>
    /// <param name="sPattern">Pattern to optionally match</param>
    /// <param name="eGreediness">Matching type</param>
    /// <returns>RegexBuilder with optional pattern appended</returns>
    /// <remarks>
    /// Appends the pattern inside of a non-capturing group if it is longer than one character.
    /// Optional("abc", MatchingBehavior.GREEDY) = (?:abc)?
    /// Optional("abc", MatchingBehavior.LAZY) = (?:abc)??
    /// Optional("\s+", MatchingBehavior.GREEDY) = \s*
    /// Optional("\s+", MatchingBehavior.LAZY) = \s*?
    /// </remarks>
    public RegexBuilder Optional(string sPattern, MatchingBehavior eBehavior)
    {
      switch (eBehavior)
      {
        case MatchingBehavior.GREEDY:
          return OptionalGreedy(sPattern);
        case MatchingBehavior.LAZY:
          return OptionalLazy(sPattern);
      }
      return OptionalGreedy(sPattern);
    }

    public RegexBuilder Optional(RegexBuilder oRBOther)
    {
      return Optional(oRBOther, MatchingBehavior.GREEDY);
    }

    public RegexBuilder Optional(RegexBuilder oRBOther, MatchingBehavior eBehavior)
    {
      return Optional(oRBOther.ToString(), eBehavior);
    }

    /// <summary>
    /// Begins a non-capturing optional group. There must be an equal number of EndOptional() as BeginOptional()
    /// </summary>
    /// <returns></returns>
    public RegexBuilder BeginOptional()
    {
      GroupCount++;
      return Append(@"(?:");
    }

    /// <summary>
    /// Ends a non-capturing optional group. There must be an equal number of EndOptional() as BeginOptional()
    /// </summary>
    /// <returns></returns>
    public RegexBuilder EndOptional()
    {
      GroupCount--;
      return Append(@")?");
    }

    #endregion

    #region ZeroOrMore

    private RegexBuilder ZeroOrMoreGreedy(string sPattern)
    {
      if (sPattern.Equals(WS))
        return Append(OPTIONAL_WS);
      if (sPattern.Length < 2 || (sPattern.Length == 2 && sPattern.StartsWith(@"\")))
        return Append(sPattern + "*");
      return AppendFormat(@"(?:{0})*", sPattern);
    }

    private RegexBuilder ZeroOrMoreLazy(string sPattern)
    {
      if (sPattern.Equals(WS))
        return Append(OPTIONAL_WS + "?");
      if (sPattern.Length < 2 || (sPattern.Length == 2 && sPattern.StartsWith(@"\")))
        return Append(sPattern + "*?");
      return AppendFormat(@"(?:{0})*?", sPattern);
    }

    /// <summary>
    /// Matches the pattern 0 or more times, can be either greedy or lazy.
    /// </summary>
    /// <param name="sPattern"></param>
    /// <param name="eBehavior"></param>
    /// <returns></returns>
    /// <remarks>
    /// ZeroOrMore("abc", MatchingBehavior.GREEDY) = (?:abc)*
    /// ZeroOrMore("a", MatchingBehavior.GREEDY) = a*
    /// ZeroOrMore("abc", MatchingBehavior.LAZY) = (?:abc)*?
    /// ZeroOrMore("a", MatchingBehavior.LAZY) = a*?
    /// </remarks>
    public RegexBuilder ZeroOrMore(string sPattern, MatchingBehavior eBehavior)
    {
      switch (eBehavior)
      {
        case MatchingBehavior.GREEDY:
          return ZeroOrMoreGreedy(sPattern);
        case MatchingBehavior.LAZY:
          return ZeroOrMoreLazy(sPattern);
      }
      return ZeroOrMoreGreedy(sPattern);
    }

    /// <summary>
    /// Matches the pattern 0 or more times. Default behavior is greedy.
    /// </summary>
    /// <param name="sPattern"></param>
    /// <param name="eBehavior"></param>
    /// <returns></returns>
    /// <remarks>
    /// ZeroOrMore("abc", MatchingBehavior.GREEDY) = (?:abc)*
    /// ZeroOrMore("a", MatchingBehavior.GREEDY) = a*
    /// </remarks>
    public RegexBuilder ZeroOrMore(string sPattern)
    {
      return ZeroOrMoreGreedy(sPattern);
    }

    public RegexBuilder ZeroOrMore(RegexBuilder oPattern)
    {
      return ZeroOrMore(oPattern.ToString());
    }

    public RegexBuilder ZeroOrMore(RegexBuilder oPattern, MatchingBehavior eBehavior)
    {
      return ZeroOrMore(oPattern.ToString(), eBehavior);
    }

    private RegexBuilder ZeroOrMore()
    {
      return ZeroOrMore(string.Empty);
    }

    public RegexBuilder ZeroOrMore(MatchingBehavior eBehavior)
    {
      return ZeroOrMore(string.Empty, eBehavior);
    }

    #endregion

    #region OneOrMore

    private RegexBuilder OneOrMoreGreedy(string sPattern)
    {
      if (sPattern.Equals(WS))
        return Append(WS);
      if (sPattern.Length < 2 || (sPattern.Length == 2 && sPattern.StartsWith(@"\")))
        return Append(sPattern + "+");
      return AppendFormat(@"(?:{0})+", sPattern);
    }

    private RegexBuilder OneOrMoreLazy(string sPattern)
    {
      if (sPattern.Equals(WS))
        return Append(WS + "?");
      if (sPattern.Length < 2 || (sPattern.Length == 2 && sPattern.StartsWith(@"\")))
        return Append(sPattern + "+?");
      return AppendFormat(@"(?:{0})+?", sPattern);
    }

    /// <summary>
    /// Matches the pattern 1 or more times. Default behavior is greedy.
    /// </summary>
    /// <param name="sPattern"></param>
    /// <param name="eBehavior"></param>
    /// <returns></returns>
    /// <remarks>
    /// OneOrMore("abc", MatchingBehavior.GREEDY) = (?:abc)+
    /// OneOrMore("a", MatchingBehavior.GREEDY) = a+
    /// </remarks>
    public RegexBuilder OneOrMore(string sPattern)
    {
      return OneOrMoreGreedy(sPattern);
    }

    /// <summary>
    /// Matches the pattern 1 or more times, can be either greedy or lazy.
    /// </summary>
    /// <param name="sPattern"></param>
    /// <param name="eBehavior"></param>
    /// <returns></returns>
    /// <remarks>
    /// OneOrMore("abc", MatchingBehavior.GREEDY) = (?:abc)+
    /// OneOrMore("a", MatchingBehavior.GREEDY) = a+
    /// OneOrMore("abc", MatchingBehavior.LAZY) = (?:abc)+?
    /// OneOrMore("a", MatchingBehavior.LAZY) = a+?
    /// </remarks>
    public RegexBuilder OneOrMore(string sPattern, MatchingBehavior eBehavior)
    {
      switch (eBehavior)
      {
        case MatchingBehavior.GREEDY:
          return OneOrMoreGreedy(sPattern);
        case MatchingBehavior.LAZY:
          return OneOrMoreLazy(sPattern);
      }
      return OneOrMoreGreedy(sPattern);
    }

    public RegexBuilder OneOrMore(RegexBuilder oPattern)
    {
      return OneOrMore(oPattern.ToString());
    }

    public RegexBuilder OneOrMore(RegexBuilder oPattern, MatchingBehavior eBehavior)
    {
      return OneOrMore(oPattern.ToString(), eBehavior);
    }

    public RegexBuilder OneOrMore()
    {
      return OneOrMore(string.Empty);
    }

    public RegexBuilder OneOrMore(MatchingBehavior eBehavior)
    {
      return OneOrMore(string.Empty, eBehavior);
    }

    #endregion

    #region MatchNTimes

    /// <summary>
    /// Matches the pattern exactly n times.
    /// </summary>
    /// <param name="sPattern"></param>
    /// <param name="eBehavior"></param>
    /// <returns></returns>
    /// <remarks>
    /// MatchNTimes("abc", 3) = (?:abc){3}
    /// MatchNTimes("a", 3) = a{3}
    /// </remarks>
    public RegexBuilder MatchNTimes(string sPattern, int n)
    {
      if (sPattern.Equals(WS))
        return AppendFormat(@"\s{{{0}}}", n);
      if (sPattern.Length == 1 || (sPattern.Length == 2 && sPattern.StartsWith(@"\")))
        return AppendFormat(@"{0}{{{1}}}", sPattern, n);
      return AppendFormat(@"(?:{0}){{{1}}}", sPattern, n);
    }

    public RegexBuilder MatchNTimes(RegexBuilder oPattern, int n)
    {
      return MatchNTimes(oPattern.ToString(), n);
    }

    public RegexBuilder MatchNTimes(int n)
    {
      return AppendFormat("{{{0}}}", n);
    }

    #endregion

    #region MatchBetweenNAndMTimes

    private RegexBuilder MatchBetweenNAndMTimesGreedy(string sPattern, int n, int m)
    {
      if (sPattern.Equals(WS))
        return AppendFormat(@"\s{{{0},{1}}}", n, m);
      if (sPattern.Length < 2 || (sPattern.Length == 2 && sPattern.StartsWith(@"\")))
        return AppendFormat(@"{0}{{{1},{2}}}", sPattern, n, m);
      return AppendFormat(@"(?:{0}){{{1},{2}}}", sPattern, n, m);
    }

    private RegexBuilder MatchBetweenNAndMTimesLazy(string sPattern, int n, int m)
    {
      if (sPattern.Equals(WS))
        return AppendFormat(@"\s{{{0},{1}}}?", n, m);
      if (sPattern.Length < 2 || (sPattern.Length == 2 && sPattern.StartsWith(@"\")))
        return AppendFormat(@"{0}{{{1},{2}}}?", sPattern, n, m);
      return AppendFormat(@"(?:{0}){{{1},{2}}}?", sPattern, n, m);
    }

    /// <summary>
    /// Matches the pattern between n and m times, can be either greedy or lazy.
    /// </summary>
    /// <param name="sPattern"></param>
    /// <param name="eBehavior"></param>
    /// <returns></returns>
    /// <remarks>
    /// MatchBetweenNAndMTimes("abc", 3, 5, MatchingBehavior.GREEDY) = (?:abc){3,5}
    /// MatchBetweenNAndMTimes("a", 3, 5, MatchingBehavior.GREEDY) = a{3,5}
    /// MatchBetweenNAndMTimes("abc", 3, 5, MatchingBehavior.LAZY) = (?:abc){3,5}?
    /// MatchBetweenNAndMTimes("a", 3, 5, MatchingBehavior.LAZY) = a{3,5}?
    /// </remarks>
    public RegexBuilder MatchBetweenNAndMTimes(string sPattern, int n, int m, MatchingBehavior eBehavior)
    {
      switch (eBehavior)
      {
        case MatchingBehavior.GREEDY:
          return MatchBetweenNAndMTimesGreedy(sPattern, n, m);
        case MatchingBehavior.LAZY:
          return MatchBetweenNAndMTimesLazy(sPattern, n, m);
      }
      return MatchBetweenNAndMTimesGreedy(sPattern, n, m);
    }

    /// <summary>
    /// Matches the pattern between n and m times. Default behavior is greedy.
    /// </summary>
    /// <param name="sPattern"></param>
    /// <param name="eBehavior"></param>
    /// <returns></returns>
    /// <remarks>
    /// MatchBetweenNAndMTimes("abc", 3, 5) = (?:abc){3,5}
    /// MatchBetweenNAndMTimes("a", 3, 5) = a{3,5}
    /// </remarks>
    public RegexBuilder MatchBetweenNAndMTimes(string sPattern, int n, int m)
    {
      return MatchBetweenNAndMTimesGreedy(sPattern, n, m);
    }

    public RegexBuilder MatchBetweenNAndMTimes(RegexBuilder oPattern, int n, int m)
    {
      return MatchBetweenNAndMTimes(oPattern.ToString(), n, m);
    }

    public RegexBuilder MatchBetweenNAndMTimes(RegexBuilder oPattern, int n, int m, MatchingBehavior eBehavior)
    {
      return MatchBetweenNAndMTimes(oPattern.ToString(), n, m, eBehavior);
    }

    public RegexBuilder MatchBetweenNAndMTimes(int n, int m)
    {
      return MatchBetweenNAndMTimes(string.Empty, n, m);
    }

    public RegexBuilder MatchBetweenNAndMTimes(int n, int m, MatchingBehavior eBehavior)
    {
      return MatchBetweenNAndMTimes(string.Empty, n, m, eBehavior);
    }

    #endregion

    #region MatchAtLeastNTimes

    private RegexBuilder MatchAtLeastNTimesGreedy(string sPattern, int n)
    {
      if (sPattern.Equals(WS))
        return AppendFormat(@"\s{{{0},}}", n);
      if (sPattern.Length == 1 || (sPattern.Length == 2 && sPattern.StartsWith(@"\")))
        return AppendFormat(@"{0}{{{1},}}", sPattern, n);
      return AppendFormat(@"(?:{0}){{{1},}}", sPattern, n);
    }

    private RegexBuilder MatchAtLeastNTimesLazy(string sPattern, int n)
    {
      if (sPattern.Equals(WS))
        return AppendFormat(@"\s{{{0},}}?", n);
      if (sPattern.Length == 1 || (sPattern.Length == 2 && sPattern.StartsWith(@"\")))
        return AppendFormat(@"{0}{{{1},}}?", sPattern, n);
      return AppendFormat(@"(?:{0}){{{1},}}?", sPattern, n);
    }

    /// <summary>
    /// Matches the pattern at least n times, can be either greedy or lazy.
    /// </summary>
    /// <param name="sPattern"></param>
    /// <param name="eBehavior"></param>
    /// <returns></returns>
    /// <remarks>
    /// MatchAtLeastNTimes("abc", 3, MatchingBehavior.GREEDY) = (?:abc){3,}
    /// MatchAtLeastNTimes("a", 3, MatchingBehavior.GREEDY) = a{3,}
    /// MatchAtLeastNTimes("abc", 3, MatchingBehavior.LAZY) = (?:abc){3,}?
    /// MatchAtLeastNTimes("a", 3, MatchingBehavior.LAZY) = a{3,}?
    /// </remarks>
    public RegexBuilder MatchAtLeastNTimes(string sPattern, int n, MatchingBehavior eBehavior)
    {
      switch (eBehavior)
      {
        case MatchingBehavior.GREEDY:
          return MatchAtLeastNTimesGreedy(sPattern, n);
        case MatchingBehavior.LAZY:
          return MatchAtLeastNTimesLazy(sPattern, n);
      }
      return MatchAtLeastNTimesGreedy(sPattern, n);
    }

    /// <summary>
    /// Matches the pattern at least n times. Default behavior is greedy.
    /// </summary>
    /// <param name="sPattern"></param>
    /// <param name="eBehavior"></param>
    /// <returns></returns>
    /// <remarks>
    /// MatchAtLeastNTimes("abc", 3) = (?:abc){3,}
    /// MatchAtLeastNTimes("a", 3) = a{3,}
    /// </remarks>
    public RegexBuilder MatchAtLeastNTimes(string sPattern, int n)
    {
      return MatchAtLeastNTimesGreedy(sPattern, n);
    }

    public RegexBuilder MatchAtLeastNTimes(RegexBuilder oPattern, int n)
    {
      return MatchAtLeastNTimes(oPattern.ToString(), n);
    }

    public RegexBuilder MatchAtLeastNTimes(RegexBuilder oPattern, int n, MatchingBehavior eBehavior)
    {
      return MatchAtLeastNTimes(oPattern.ToString(), n, eBehavior);
    }

    #endregion

    #region PositiveLookahead

    public RegexBuilder PositiveLookahead(string sPattern)
    {
      return AppendFormat(@"(?={0})", sPattern);
    }

    public RegexBuilder PositiveLookahead(RegexBuilder oPattern)
    {
      return PositiveLookahead(oPattern.ToString());
    }

    #endregion

    #region NegativeLookahead

    public RegexBuilder NegativeLookahead(string sPattern)
    {
      return AppendFormat(@"(?!{0})", sPattern);
    }

    public RegexBuilder NegativeLookahead(RegexBuilder oPattern)
    {
      return NegativeLookahead(oPattern.ToString());
    }

    #endregion

    #region PositiveLookbehind

    public RegexBuilder PositiveLookbehind(string sPattern)
    {
      return AppendFormat(@"(?<={0})", sPattern);
    }

    public RegexBuilder PositiveLookbehind(RegexBuilder oPattern)
    {
      return PositiveLookbehind(oPattern.ToString());
    }

    #endregion

    #region NegativeLookbehind

    public RegexBuilder NegativeLookbehind(string sPattern)
    {
      return AppendFormat(@"(?<!{0})", sPattern);
    }

    public RegexBuilder NegativeLookbehind(RegexBuilder oPattern)
    {
      return NegativeLookbehind(oPattern.ToString());
    }

    #endregion

    #region BackReference

    public RegexBuilder BackReference(int iGroupNumber)
    {
      return AppendFormat(@"\{0}", iGroupNumber);
    }

    public RegexBuilder BackReference(string sGroupName)
    {
      return AppendFormat(@"\k<{0}>", sGroupName);
    }

    #endregion

    #region NonCapturingGroup

    public RegexBuilder NonCapturingGroup(string sPattern)
    {
      return AppendFormat(@"(?:{0})", sPattern);
    }

    public RegexBuilder NonCapturingGroup(RegexBuilder oPattern)
    {
      return NonCapturingGroup(oPattern.ToString());
    }

    public RegexBuilder BeginNonCapturingGroup()
    {
      GroupCount++;
      return Append(@"(?:");
    }

    public RegexBuilder EndNonCapturingGroup()
    {
      GroupCount--;
      return Append(@")");
    }

    #endregion

    #region CapturingGroup

    public RegexBuilder CapturingGroup(string sGroupName, string sPattern)
    {
      if (string.IsNullOrEmpty(sGroupName))
        return AppendFormat(@"({0})", sGroupName);
      return AppendFormat(@"(?<{0}>{1})", sGroupName, sPattern);
    }

    public RegexBuilder CapturingGroup(string sGroupName, RegexBuilder oPattern)
    {
      return CapturingGroup(sGroupName, oPattern.ToString());
    }

    public RegexBuilder BeginCapturingGroup(string sGroupName)
    {
      GroupCount++;
      if (string.IsNullOrEmpty(sGroupName))
        return Append(@"(");
      return AppendFormat(@"(?<{0}>", sGroupName);
    }

    public RegexBuilder EndCapturingGroup()
    {
      GroupCount--;
      return Append(@")");
    }

    #endregion

    #region Build

    public Regex Build(RegexOptions options, TimeSpan matchTimeout)
    {
      return new Regex(Pattern.ToString(), options, matchTimeout);
    }

    public Regex Build(RegexOptions options)
    {
      return new Regex(Pattern.ToString(), options);
    }

    public Regex Build()
    {
      return new Regex(Pattern.ToString());
    }

    #endregion

    public string ToString()
    {
      return Pattern.ToString();
    }

  }
}